﻿using System;
using System.Collections.Generic;
using System.Xml;
using SdmxMl.Common;
using SdmxMl.Helper;

namespace SdmxMl.Structure
{
    /// <summary>
    /// An identifier component describes the use of an object within the full target
    /// identifier set. An identifier component must be one of the formally-recognized
    /// object classes found in the SDMX Information Model: the sub-element TargetObjectClass
    /// provides a way of indicating which objects will be used in reporting metadata, and
    /// will indicate how those objects are to be identified by the metadata reporters
    /// (which value sets will be allowed for each identification field for each object).
    /// The RepresentationScheme child element is used to indicate the valid range of values
    /// for the providing taget identifiers in reported metadata.
    /// </summary>
    public class IdentifierComponentType : BaseArtefact
    {
        #region Properties

        public virtual string Urn  { get; set; }

        public virtual string Uri { get; set; }

        public ObjectTypeCodelistType TargetObjectClass  { get; set; }

        public RepresentationSchemeType RepresentationScheme  { get; set; }

        #endregion // Properties

        #region Construction

        private void Init()
        {
            RepresentationScheme = new RepresentationSchemeType();
            TargetObjectClass = ObjectTypeCodelistType.Any;
        }

        public IdentifierComponentType()
        {
            Init();
        }

        public IdentifierComponentType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
            : base(nodeArtefact, nsmgr)
        {
            Init();
            Urn = Xmlhelper.GetAtt(nodeArtefact, "urn");
            Uri = Xmlhelper.GetAtt(nodeArtefact, "uri");

            if (nsmgr.VersionSdmx == SdmxVersion.SDMX_20)
            {
                string str = Xmlhelper.GetNodeText(nodeArtefact, "./structure:TargetObjectClass", nsmgr).ToLower();

                // Enumeration changed between 2.0 and 2.1, even in casing
                foreach (ObjectTypeCodelistType otc in Enum.GetValues(typeof(ObjectTypeCodelistType)))
                    if (otc.ToString().ToLower() == str)
                    {
                        TargetObjectClass = otc;
                        break;
                    }

                XmlNode noder = nodeArtefact.SelectSingleNode("./structure:RepresentationScheme", nsmgr);
                if (noder != null)
                    RepresentationScheme = new RepresentationSchemeType(noder, nsmgr);
            }
            else // SDMX 2.1
            {
                Name.UpdateDefaultText("SDMX2.1 undefined");

                string str = Xmlhelper.GetAtt(nodeArtefact, "objectType").ToLower(); 
                foreach (ObjectTypeCodelistType otc in Enum.GetValues(typeof(ObjectTypeCodelistType)))
                    if (otc.ToString().ToLower() == str)
                    {
                        TargetObjectClass = otc;
                        break;
                    }
                XmlNode noder = nodeArtefact.SelectSingleNode("./structure:LocalRepresentation", nsmgr);
                if (noder != null)
                    RepresentationScheme = new RepresentationSchemeType(noder, nsmgr);
            }
        }


        #endregion //Construction

        #region XML serialize

        public void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);
            // id required
            if (string.IsNullOrEmpty(Id))
                Id = "undefined";
            xw.WriteAttributeString("id", Id);
            Xmlhelper.WriteAttributeIfExist(xw, "uri", Uri);
            Xmlhelper.WriteAttributeIfExist(xw, "urn", Urn);

            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_20)
            {
                Name.WriteXml(xw, prefix, "Name");
                Description.WriteXml(xw, prefix, "Description");

                // as we use a 2.1 enumeration, try to find a match
                string target = "Component"; // choosen as dummy
                string newType = TargetObjectClass.ToString().ToLower();
                // Scan old 2.0 emun
                foreach (ObjectIDType otc in Enum.GetValues(typeof(ObjectIDType)))
                    if (otc.ToString().ToLower() == newType)
                    {
                        target = otc.ToString();
                        break;
                    }

                xw.WriteElementString(prefix, "TargetObjectClass", null, target);
                RepresentationScheme.WriteXml(xw, prefix, "RepresentationScheme");

                // Annotations
                AnnotationList.WriteXml(xw, prefix, null);
            }
            else // 2.1
            {
                xw.WriteAttributeString("objectType", TargetObjectClass.ToString());
                // Annotations
                AnnotationList.WriteXml(xw, "common", null);

                // Reference to representation if defined or simple TextFormat
                RepresentationScheme.WriteXml(xw, prefix, "LocalRepresentation");
            }

            xw.WriteEndElement();
        }
        #endregion //XML serialize

    }

    /// <summary> List of Identifier Component.</summary>
    public class IdentifierComponentTypeList : List<IdentifierComponentType>
    {
        public IdentifierComponentType LocateIdentifier(string componentId)
        {
            foreach (IdentifierComponentType ict in this)
                if (ict.Id == componentId)
                    return ict;

            return null;
        }

    }
}
